﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Rud.Holmgren.TabulexLib.Common
{
    public interface IPromise<T>
    {
        IPromise<T> Done(Action<T> callback);
        IPromise<T> Fail(Action<PromiseFailArgs> callback);
        IPromise<T> Always(Action callback);

    }

    public class Deferred<T> : IPromise<T>
    {
        private enum State { Pending, Success, Error };

        private State state = State.Pending;

        private T result;
        private PromiseFailArgs error;

        private Action<T> doneCallback;
        private Action<PromiseFailArgs> failCallback;
        private Action alwaysCallback;
         
        public IPromise<T> Done(Action<T> callback)
        {
            lock (this)
            {
                switch (state)
                {
                    case State.Pending:
                        this.doneCallback = callback;
                        break;

                    case State.Success:
                        callback(result);
                        break;

                    default:
                        break;
                }
            }

            return this;
        }

        public IPromise<T> Fail(Action<PromiseFailArgs> callback)
        {
            lock (this)
            {
                switch (state)
                {
                    case State.Pending:
                        this.failCallback = callback;
                        break;

                    case State.Error:
                        callback(error);
                        break;

                    default:
                        break;
                }
            }

            return this;
        }

        public IPromise<T> Always(Action callback)
        {
            lock (this)
            {
                switch (state)
                {
                    case State.Pending:
                        this.alwaysCallback = callback;
                        break;

                    case State.Success:
                    case State.Error:
                        callback();
                        break;

                    default:
                        break;
                }
            }

            return this;
        }

        public void Resolve(T value)
        {
            lock (this)
            {
                if (this.state != State.Pending) { return; }

                this.result = value;
                this.state = State.Success;

                if (doneCallback != null)
                {
                    doneCallback(this.result);
                }

                if (alwaysCallback != null)
                {
                    alwaysCallback();
                }
            }
        }

        public void Reject(PromiseFailArgs args)
        {
            lock (this)
            {
                if (this.state != State.Pending) { return; }

                this.error = args;
                this.state = State.Error;

                if (failCallback != null)
                {
                    failCallback(this.error);
                }

                if (alwaysCallback != null)
                {
                    alwaysCallback();
                }
            }
        }

        public void Reject(Exception e)
        {
            Reject(new PromiseFailArgs(e.Message, e.ToString()));
        }

#if false
        public void AsyncCallback(IAsyncResult asyncHandle)
        {
            lock (this)
            {
                try
                {
                    this.result = endAsync(asyncHandle);
                    this.state = State.Success;

                    if (doneCallback != null)
                    {
                        Dispatch(() => doneCallback(this.result));
                    }
                }
                catch (Exception e)
                {
                    this.error = new TbxDispatchFailArgs()
                    {
                        ErrorMessage = e.Message,
                        ErrorDetailsForDebugging = e.ToString()
                    };
                    this.state = State.Error;

                    if (failCallback != null)
                    {
                        Dispatch(() => failCallback(this.error));
                    }
                }

                if (alwaysCallback != null)
                {
                    Dispatch(alwaysCallback);
                }
            }
        }
#endif
    }


    public class PromiseFailArgs
    {
        public PromiseFailArgs(string errorMessage, string errorDetails)
        {
            this.ErrorMessage = errorMessage;
            this.ErrorDetailsForDebugging = errorDetails;
        }

        /// <summary>
        /// A simple errormessage if success is false.
        /// </summary>
        public string ErrorMessage { get; set; }

        /// <summary>
        /// An elaborate errormessage with exception information. This
        /// can be used when debugging as these automatically generated
        /// webservices can be difficult to debug.
        /// </summary>
        public string ErrorDetailsForDebugging { get; set; }
    }
}
